
package com.wlsk.license.common.utils;

import org.apache.commons.collections4.MapUtils;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.AuthSchemes;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.X509Certificate;
import java.util.Arrays;
import java.util.Map;

public final class HttpUtils {

  private static final Logger LOGGER = LoggerFactory.getLogger(HttpUtils.class);

  private HttpUtils() {
    throw new RuntimeException("This class can not be instantiated");
  }

  /**
   * 获取跳过认证的TrustManager
   *
   * @return X509TrustManager
   */
  private static X509TrustManager getSkipTrustManager() {
    return new X509TrustManager() {
      @Override
      public X509Certificate[] getAcceptedIssuers() {
        return null;
      }

      @Override
      public void checkClientTrusted(X509Certificate[] xcs, String str) {
      }

      @Override
      public void checkServerTrusted(X509Certificate[] xcs, String str) {
      }
    };
  }

  /**
   * 发送json参数的post请求
   * 
   * @param uri URI
   * @param content 请求内容
   * @return 响应的字符串
   * @throws IOException IO流异常
   */
  public static String sendJsonPost(String uri, String content) throws IOException {
    HttpPost httpPost = new HttpPost(uri);
    httpPost.setHeader("Content-Type", "application/json;charset=UTF-8");
    StringEntity se = new StringEntity(content);
    se.setContentType("text/json");
    httpPost.setEntity(se);
    HttpClient client = buildClient(uri);
    HttpResponse response = client.execute(httpPost);
    HttpEntity entity = response.getEntity();
    String s = EntityUtils.toString(entity, "UTF-8");
    return s;
  }

  public static String sendTextPost(String uri, String text) throws IOException {
    HttpPost httpPost = new HttpPost(uri);
    ContentType contentType = ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8);
    httpPost.setHeader(HttpHeaders.CONTENT_TYPE, contentType.toString());
    httpPost
        .setConfig(RequestConfig.custom().setConnectTimeout(10000).setSocketTimeout(30000).build());
    StringEntity se = new StringEntity(text, StandardCharsets.UTF_8);
    httpPost.setEntity(se);
    HttpClient client = buildClient(uri);
    HttpResponse response = client.execute(httpPost);
    HttpEntity entity = response.getEntity();
    return EntityUtils.toString(entity, StandardCharsets.UTF_8);
  }

  public static void sendGetDownloadFile(String url, Map<String, String> params, OutputStream os) {
    LOGGER.info("download-url -> {}", url);
    InputStream is = null;
    try {
      URIBuilder uriBuilder = new URIBuilder(url);
      if (MapUtils.isNotEmpty(params)) {
        for (Map.Entry<String, String> entry : params.entrySet()) {
          uriBuilder.addParameter(entry.getKey(), entry.getValue());
        }
      }
      URI uri = uriBuilder.build();
      LOGGER.info("full-url -> {}", uri.toString());
      HttpGet httpGet = new HttpGet(uri);
      httpGet.setConfig(
          RequestConfig.custom().setConnectTimeout(10 * 1000).setSocketTimeout(60 * 1000).build());
      HttpClient httpClient = buildClient(url);
      HttpResponse httpResponse = httpClient.execute(httpGet);
      int statusCode = httpResponse.getStatusLine().getStatusCode();
      if (statusCode == 200) {
        HttpEntity entity = httpResponse.getEntity();
        is = entity.getContent();
        IOUtils.copy(is, os);
        return;
      } else {
        LOGGER.warn("response code -> {}", statusCode);
        throw new RuntimeException("response is not 200");
      }
    } catch (URISyntaxException e) {
      throw new RuntimeException(e);
    } catch (ClientProtocolException e) {
      throw new RuntimeException(e);
    } catch (IOException e) {
      throw new RuntimeException(e);
    } finally {
      IOUtils.closeQuietly(is);
      IOUtils.closeQuietly(os);
    }
  }

  /**
   * 根据uri构建HttpClient
   * 
   * @param uri URI
   * @return HttpClient
   */
  private static HttpClient buildClient(String uri) {
    if (isHttps(uri)) {
      return buildClientNoSsl();
    } else {
      return HttpClients.createDefault();
    }
  }

  /**
   * 判断uri是否为https协议
   * 
   * @param uri URI
   * @return true https协议
   */
  public static boolean isHttps(String uri) {
    return uri.startsWith("https");
  }

  /**
   * 构建Https访问时跳过ssl的请求Client
   * 
   * @return HttpClient
   */
  private static HttpClient buildClientNoSsl() {
    try {
      SSLContext ctx = SSLContext.getInstance(SSLConnectionSocketFactory.TLS);
      ctx.init(null, new TrustManager[] { getSkipTrustManager() }, null);
      SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(ctx,
          NoopHostnameVerifier.INSTANCE);
      // 创建Registry
      RequestConfig requestConfig = RequestConfig.custom()
          .setCookieSpec(CookieSpecs.STANDARD_STRICT).setExpectContinueEnabled(Boolean.TRUE)
          .setTargetPreferredAuthSchemes(Arrays.asList(AuthSchemes.NTLM, AuthSchemes.DIGEST))
          .setProxyPreferredAuthSchemes(Arrays.asList(AuthSchemes.BASIC)).build();
      Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
          .<ConnectionSocketFactory> create()
          .register("http", PlainConnectionSocketFactory.INSTANCE).register("https", socketFactory)
          .build();
      // 创建ConnectionManager，添加Connection配置信息
      PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(
          socketFactoryRegistry);
      HttpClient closeableHttpClient = HttpClients.custom().setConnectionManager(connectionManager)
          .setDefaultRequestConfig(requestConfig).build();
      return closeableHttpClient;
    } catch (KeyManagementException e) {
      throw new RuntimeException(e);
    } catch (NoSuchAlgorithmException e) {
      throw new RuntimeException(e);
    }
  }
}
